package org.openzip.openzip.gui.util.checkBox;

import java.util.*;
import java.awt.event.*;

import org.openzip.openzip.gui.util.checkBox.event.ButtonStateChangedListener;

/**
 * Class extending <code>BasicCheckBoxModel</code> by a third state - undecided.
 * The third state is used for folders that has only some of their files
 * selected. <br>
 * This class support two kinds of events. <code>ActionEvent</code> will occur
 * when the button selection state changes. Each <code>State</code> has its
 * selection state defined.<br>
 * <code>ButtonStateChangedEvent</code> occurs when the state of the button
 * changes. This is usually accompanied by <code>ActionEvent</code> when user
 * clicks the button, but setting such state which has the same selection state
 * as the prevoius state will fire the <code>ButtonStateChangedEvent</code>, but
 * not the <code>ActionEvent</code>.<br>
 * If someone needs display this checkbox as a component, there is method 
 * getGraphicalRepresentation, which will return a Component representing
 * this checkbox. However, this component won't respond to mouse events (such
 * as clicks) automatically. Custom listener must be added.
 * 
 * @see State
 * @see ButtonStateChangedListener
 * @author Jakub Závěrka
 */
public class ThreestateCheckBoxModel extends BasicCheckBoxModel
{
	//component for painting
	private static ThreestateCheckBox checkBox = new ThreestateCheckBox();
	private State currentState = State.NOT_SELECTED;
	
	//listeners for state changes
	private Collection<ButtonStateChangedListener> buttonStateChangedListeners
		= new HashSet<ButtonStateChangedListener>();
	
	/**
	 * Constructor.
	 * 
	 * @param initial
	 *           Initial state of this checkbox.
	 */
	public ThreestateCheckBoxModel(State initial){
		// set the initial state
		setState(initial);
	}

	public ThreestateCheckBoxModel(){
		this(State.NOT_SELECTED);
	}
	
	/**
	 * Will set the button to the provided state. If the state is different from
	 * the curent one, it will fire the buttonStateChanged event.
	 * 
	 * @param state
	 *           <code>State</code> to be set.
	 */
	public void setState(State state){
		//setting the same state will do nothing
		if(currentState != state){
			State oldState = currentState;
			currentState = state;
			super.setSelected(currentState.isSelected());
			//and fire buttonStateChanged
			fireButtonStateChanged(oldState, currentState);
		}
	}

	/**
	 * Adds a <code>ButtonStateChangedListener</code> to the collection.
	 */
	public void addButtonStateChangedListener(ButtonStateChangedListener e){
		buttonStateChangedListeners.add(e);
	}

	/**
	 * Removes the specified <code>ButtonStateChangedListener</code> from the 
	 * collection.
	 */
	public void removeButtonStateChangedListener(Object o){
		buttonStateChangedListeners.remove(o);
	}
	
	/**
	 * This will notify all <code>ButtonStateChangedListeners</code> that the
	 * <code>State</code> of the button has changed. 
	 * @param oldState The previous state.
	 * @param newState New state of the button.
	 */
	protected void fireButtonStateChanged(State oldState, State newState){
		ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "");
		for(ButtonStateChangedListener listener : buttonStateChangedListeners){
			listener.buttonStateChanged(e, oldState, newState);
		}
	}

	/**
	 * Returns the current state of the button.
	 */
	public State getState(){
		return currentState;
	}

	/**
	 * Overriden method from <code>JCheckBox</code> to support the third state.
	 * Instead of setting the selection state, it sets the <code>State</code> to
	 * the button.
	 * 
	 * @param b
	 *           Sets <code>State.SELECTED</code> when <code>true</code>, or
	 *           <code>State.NOT_SELECTED</code> when <code>false</code>.
	 */
	public void setSelected(boolean b){
		if(b){
			setState(State.SELECTED);
		}
		else{
			setState(State.NOT_SELECTED);
		}
	}

	/**
	 * Returns a ThreestateCheckBox with the current state as the graphical
	 * representation of this model.
	 */
	public ThreestateCheckBox getGraphicalRepresentation(){
		checkBox.setState(currentState);
		return checkBox;
	}

	/**
	 * This will set next state of the button. This method is used in clicking
	 * the button and will toggle between <code>State.SELECTED</code> and
	 * <code>State.UNSELECTED</code>. Third state (<code>State.UNDECIDED</code>)
	 * is never set (must be set programatically).
	 */
	public void nextState(){
		if(currentState == State.SELECTED){
			setSelected(false);
		}
		else{
			setSelected(true);
		}
	}
}